//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <regs.h>
#include <_atomic.h>

/*
 * The InterlockedIncrement function both increments (increases by one)
 * the value of the specified 32-bit variable and checks the resulting value.
 * InterlockedIncrement prevents more than one thread from using
 * the InterlockedDecrement or InterlockedIncrement function to
 * access the same variable simultaneously.
 */
Int32 __InterlockedIncrement(PInt32 plAddend)
{
    Int32 lOriginalAddend;

    ASM(
    "InterlockedIncrement_1:"
        "ll     $14, 0(%1);"
        "addiu  $14, $14, 1;"
        "sw     $14, 0(%0);"
        "sc     $14, 0(%1);"
        "beqz   $14, InterlockedIncrement_1;",
        &lOriginalAddend, plAddend);

    return lOriginalAddend;
}

/*
 * The InterlockedDecrement function both decrements (decreases by one)
 * the value of the specified 32-bit variable and checks the resulting value.
 * InterlockedDecrement prevents more than one thread from using
 * the InterlockedDecrement or InterlockedIncrement function to
 * access the same variable simultaneously.
 */
Int32 __InterlockedDecrement(PInt32 plAddend)
{
    Int32 lOriginalAddend;

    ASM(
    "InterlockedDecrement_1:"
        "ll     $14, (%1);"
        "addiu  $14, $14, -1;"
        "sw     $14, 0(%0);"
        "sc     $14, (%1);"
        "beqz   $14, InterlockedDecrement_1;",
        &lOriginalAddend, plAddend);

    return lOriginalAddend;
}

/*
 * The InterlockExchangeAdd function performs an atomic addition of
 * an increment value to an addend variable. The function prevents
 * more than one thread from using the same variable simultaneously.
 */
Int32 __InterlockedExchangeAdd(PInt32 plAddend, Int32 lIncrement)
{
    Int32 lOriginalAddend;

    ASM(
        "move   $14, %2;"
    "InterlockedExchangeAdd_1:"
        "ll     $15, (%1);"
        "add    $13, $15, $14;"
        "sc     $13, (%1);"
        "beqz   $13, InterlockedExchangeAdd_1;"
        "sw     $15, 0(%0);",
        &lOriginalAddend, plAddend, lIncrement);

    return lOriginalAddend;
}

/*
 * The InterlockedExchange function atomically exchanges
 * a pair of 32-bit values.  The prior value of the Int32 value
 * pointed to by Target indicates success.
 */
Int32 __InterlockedExchange(PInt32 plTarget, Int32 lValue)
{
    Int32 lOriginalTarget;

    ASM(
        "move   $14, %2;"
    "InterlockedExchange_1:"
        "ll     $13, 0(%1);"
        "or     $15, $14, $0;"
        "sc     $15, (%1);"
        "beqz   $15, InterlockedExchange_1;"
        "sw     $13, 0(%0);",
        &lOriginalTarget, plTarget, lValue);

    return lOriginalTarget;
}

/*
 * The InterlockedExchangePointer function atomically exchanges
 * a pair of values. The function prevents more than one thread
 * from using the same variable simultaneously.
 *
 * If you are exchanging pointer values, this function supersedes
 * the InterlockedExchange function.
 */
PVoid __InterlockedExchangePointer(PVoid * ppvTarget, PVoid pvValue)
{
    PVoid pvOriginalTarget;

    ASM(
        "move   $14, %2;"
    "InterlockedExchangePointer_1:"
        "ll     $13, (%1);"
        "sw     $13, 0(%0);"
        "or     $15, $14, $0;"
        "sc     $15, (%1);"
        "beqz   $15, InterlockedExchangePointer_1;",
        &pvOriginalTarget, ppvTarget, pvValue);

    return pvOriginalTarget;
}

/*
 * The InterlockedCompareExchange function performs an atomic comparison
 * of the specified values and exchanges the values, based on the outcome
 * of the comparison. The function prevents more than one thread from
 * using the same variable simultaneously.
 *
 * If you are exchanging pointer values, this function has been superseded
 * by the InterlockedCompareExchangePointer function.
 */
Int32 __InterlockedCompareExchange(
    PInt32 plDestination, Int32 lExchange, Int32 lComparand)
{
    Int32 lOriginalDestination;

    ASM(
        "move   t7, %3;"       // load the lComparand
    "InterlockedCompareExchange_2:"
        "ll     t6, (%1);"     // load the plDestination
        "sw     t6, 0(%0);"
        "bne    t7, t6, InterlockedCompareExchange_1;"
        "move   t6, %2;"
    "InterlockedCompareExchange_1:"
        "sc     t6, (%1);"
        "beqz   t6, InterlockedCompareExchange_2;",
        &lOriginalDestination, plDestination,
        lExchange, lComparand);

    return lOriginalDestination;
}

/*
 * The InterlockedCompareExchangePointer function performs an atomic
 * comparison of the specified values parameters and exchanges the
 * values, based on the outcome of the comparison. More than one
 * thread cannot use the same variable simultaneously.
 *
 * If you are exchanging pointer values, this function supersedes
 * the InterlockedCompareExchange function.
 */
PVoid __InterlockedCompareExchangePointer(
    PVoid * ppvDestination, PVoid pvExchange, PVoid pvComparand)
{
    PVoid pvOriginalDestination;

    ASM(
        "move   $15, %3;"       // load the lComparand
    "InterlockedCompareExchangePointer_2:"
        "ll     $14, (%1);"     // load the plDestination
       "sw      $14, 0(%0);"
        "bne    $15, $14, InterlockedCompareExchangePointer_1;"
        "move   $14, %2;"
    "InterlockedCompareExchangePointer_1:"
        "sc     $14, (%1);"
        "beqz   $14, InterlockedCompareExchangePointer_2;",
        &pvOriginalDestination, ppvDestination,
        pvExchange, pvComparand);

    return pvOriginalDestination;
}
